home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / general / modelers / geomview / source.lha / Geomview / src / lib / shade / light.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-29  |  16.3 KB  |  788 lines

  1. /* Copyright (c) 1992 The Geometry Center; University of Minnesota
  2.    1300 South Second Street;  Minneapolis, MN  55454, USA;
  3.    
  4. This file is part of geomview/OOGL. geomview/OOGL is free software;
  5. you can redistribute it and/or modify it only under the terms given in
  6. the file COPYING, which you should have received along with this file.
  7. This and other related software may be obtained via anonymous ftp from
  8. geom.umn.edu; email: software@geom.umn.edu. */
  9.  
  10. /* Authors: Charlie Gunn, Stuart Levy, Tamara Munzner, Mark Phillips */
  11.  
  12. /*
  13.  *    light -
  14.  *        Support for describing light sources and lists of 
  15.  *        light sources.
  16.  *
  17.  *    Pat Hanrahan 1989
  18.  */
  19. #include "appearance.h"
  20. #include "ooglutil.h"
  21.  
  22. static void norm();
  23.  
  24. /*
  25.  * Default light is full white and position along the z-axis.
  26.  */
  27.  
  28. static Color black = { 0.0, 0.0, 0.0 };
  29.  
  30. LtLight *
  31. _LtSet(LtLight *light, int a1, va_list *alist)
  32. {
  33.     
  34.     int attr;
  35.     Color *co;
  36.     Point *pt;
  37.     char **ablock = NULL;
  38.  
  39. #define NEXT(type) OOGL_VA_ARG(type,alist,ablock)
  40.     
  41.     if (light == NULL) {
  42.       /*
  43.        * New LtLight created here.
  44.        */
  45.       light = OOGLNewE(LtLight, "LtCreate LtLight");
  46.       LtDefault(light);
  47.     }
  48.     
  49.     for(attr = a1; attr != LT_END; attr = NEXT(int)) {
  50.     switch (attr) { /* parse argument list */
  51.       case LT_ABLOCK:
  52.         ablock = NEXT(char **);
  53.         break;
  54.       case LT_AMBIENT:
  55.         co = NEXT(Color *);
  56.         CoCopy(co, &light->ambient);
  57.         light->changed = 1;
  58.         break;
  59.       case LT_COLOR:
  60.         co = NEXT(Color *);
  61.         CoCopy(co, &light->color);
  62.         light->changed = 1;
  63.         break;
  64.       case LT_POSITION:
  65.         pt = NEXT(Point *);
  66.         PtCopy(pt, &light->position);
  67.         light->changed = 1;
  68.         break;
  69.       case LT_INTENSITY:
  70.         light->intensity = NEXT(double);
  71.         light->changed = 1;
  72.         break;
  73.       case LT_LOCATION:
  74.         light->location = NEXT(int);
  75.         light->changed = 1;
  76.         break;
  77.       default:
  78.         OOGLError (0, "_LtSet: undefined option: %d\n", attr);
  79.         return NULL;
  80.         break;
  81.     }
  82.     }
  83.     return light;
  84. #undef NEXT
  85. }
  86.  
  87. LtLight *
  88. LtCreate(int a1, ... )
  89. {
  90.     va_list alist;
  91.     LtLight *light;
  92.     
  93.     va_start(alist,a1);
  94.     light = _LtSet(NULL, a1, &alist);
  95.     va_end(alist);
  96.     return light;
  97. }
  98.  
  99. LtLight *
  100. LtSet(LtLight *light, int attr, ...)
  101. {
  102.     va_list alist;
  103.     
  104.     va_start(alist,attr);
  105.     light = _LtSet(light,attr,&alist);
  106.     va_end(alist);
  107.     return light;
  108. }
  109.  
  110. int
  111. LtGet(LtLight *light, int attr, void * value)
  112. {
  113.     if (!light) return NULL;
  114.  
  115.     switch (attr) { 
  116.        case LT_AMBIENT:
  117.     *(Color *) value = light->ambient;
  118.     break;
  119.       case LT_COLOR:
  120.     *(Color *) value = light->color;
  121.     break;
  122.       case LT_POSITION:
  123.     *(Point *) value = light->position;
  124.     break;
  125.       case LT_INTENSITY:
  126.     *(double *) value = light->intensity;
  127.     break;
  128.       case LT_LOCATION:
  129.     *(int *)value = light->location;
  130.     break;
  131.       default:
  132.     OOGLError (0, "LtGet: undefined option: %d\n", attr);
  133.     return -1;
  134.     break;
  135.       }
  136.     return 1;
  137. }
  138.  
  139. void
  140. LtDelete(LtLight *l)
  141. {
  142.     free(l);
  143. }
  144.  
  145. LtLight *
  146. LtCopy( register LtLight *l1, register LtLight *l2 )
  147. {
  148.     if(l2 == NULL)
  149.     l2 = OOGLNewE(LtLight, "LtCopy LtLight");
  150.     *l2 = *l1;        /* Don't reset the 'changed' & 'Private' fields */
  151.     l2->next = NULL;
  152.     /* Reset private and changed flags */
  153.     l2->Private = 0;
  154.     l2->changed = 1;
  155.     return l2;
  156. }
  157.  
  158.  
  159. LtLight *
  160. LtMerge( register LtLight *l1, register LtLight *l2 )
  161. {
  162.     if(l2 == NULL)
  163.     l2 = OOGLNewE(LtLight, "LtMerge LtLight");
  164.     *l2 = *l1;        
  165.     /* Don't reset the 'changed' & 'Private' fields */
  166.     l2->next = NULL;
  167.     return l2;
  168. }
  169.  
  170. LtLight *
  171. LtDefault( LtLight *light ) 
  172. {
  173.   static HPoint3 defposition = { 0.0, 0.0, 1.0, 0.0 };
  174.   static Color deflight = { 1.0, 1.0, 1.0 };
  175.  
  176.   light->next = NULL;
  177.   light->intensity = 1.0;
  178.   light->ambient = black;
  179.   light->color = deflight;
  180.   light->position = defposition;
  181.   light->location = LTF_GLOBAL;
  182.   light->Private = 0;
  183.   light->changed = 1;
  184. }
  185.  
  186. /*
  187.  * Set the intensity, color and position of a light.
  188.  */
  189. void
  190. LtProperties( LtLight *light, float intensity, Color *color, Point *position )
  191. {
  192.     light->intensity = intensity;
  193.     light->color = *color;
  194.     light->position = *position;
  195.     light->location = LTF_GLOBAL;
  196.  
  197.     light->changed = 1;
  198. }
  199.  
  200. /*
  201.  * Load a lighting description from a file.
  202.  */
  203. LtLight *
  204.   LtLoad(LtLight *li, char *name)
  205. {
  206.   FILE *f;
  207.   
  208.   if(name == NULL || (f = fopen(name, "r")) == NULL) {
  209.     OOGLError(1, "Can't find light file %s: %s", name, sperror());
  210.     return NULL;
  211.   }
  212.   li = LtFLoad(li, f, name);
  213.   fclose(f);
  214.   return li;
  215. }
  216.  
  217. /*
  218.  * Load Light from file.
  219.  * Syntax:
  220.  *    < "filename_containing_material"    [or]
  221.  *    {   keyword  value   keyword  value   ...  }
  222.  *
  223.  */
  224.  
  225. LtLight *
  226. LtFLoad(lite, f, fname)
  227.     LtLight *lite;
  228.     FILE *f;
  229.     char *fname;    /* Used for error msgs, may be NULL */
  230. {
  231.   char *w;
  232.   register int i,j;
  233.   float v[4];
  234.   int brack = 0;
  235.   static char *lkeys[] = {
  236.     "ambient", "color", "position", "location", "global", "camera", "local"
  237.     };
  238.   static short largs[] = { 3, 3, 4, 0, ~LTF_GLOBAL, ~LTF_CAMERA, ~LTF_LOCAL };
  239.   int got;
  240.   LtLight l;
  241.   
  242.   LtDefault(&l);
  243.   
  244.   for(;;) {
  245.     switch(fnextc(f, 0)) {
  246.     case '<':
  247.       fgetc(f);
  248.       if(LtLoad(&l, fdelimtok("(){}", f, 0)) == NULL) return NULL;
  249.       if(!brack) goto done;
  250.       break;
  251.     case '{': brack++; fgetc(f); break;
  252.     case '}': if(brack) { fgetc(f); } goto done;
  253.     default:
  254.       w = ftoken(f, 0);
  255.       if(w == NULL)
  256.     goto done;
  257.       
  258.       for(i = sizeof(lkeys)/sizeof(lkeys[0]); --i >= 0; )
  259.     if(!strcmp(w, lkeys[i]))
  260.       break;
  261.       
  262.       if( i < 0) {
  263.     OOGLSyntax(f, "Reading light from %s: unknown keyword %s",fname,w);
  264.     return NULL;
  265.       } else if( largs[i] > 0 && (got=fgetnf(f, largs[i], v, 0)) != largs[i] ) {
  266.     OOGLSyntax(f, "Reading light from %s: \"%s\" expects %d values, got %d",
  267.           fname, w, largs[i], got);
  268.     return NULL;
  269.       }
  270.       switch(i) {
  271.       case 0: l.ambient = *(Color *)v; break;
  272.       case 1: l.color = *(Color *)v; 
  273.     norm( &l.color, &l.intensity ); break;
  274.       case 2: l.position = *(Point *)v; break;
  275.       case 3: break;
  276.       default: l.location = ~largs[i]; break;
  277.       }
  278.     }
  279.   }
  280.  done:
  281.   lite = LtCopy(&l, lite);
  282.   return lite;
  283. }
  284.  
  285. void
  286. LtFSave( LtLight *l, FILE *f )
  287. {
  288.     fprintf(f,"\t\tambient %f %f %f\n",
  289.     l->ambient.r,
  290.     l->ambient.g,
  291.     l->ambient.b);
  292.     fprintf(f,"\t\tcolor %f %f %f\n",
  293.     l->intensity*l->color.r,
  294.     l->intensity*l->color.g,
  295.     l->intensity*l->color.b);
  296.     fprintf(f,"\t\tposition %f %f %f %f\n",
  297.     l->position.x,
  298.     l->position.y,
  299.     l->position.z,
  300.     l->position.w);
  301.     if(l->location != LTF_GLOBAL)
  302.     fprintf(f, "\t\tlocation %s\n",
  303.         l->location == LTF_CAMERA ? "camera" : "local");
  304.     /*
  305.     fprintf(f,"intensity %f\n", la->intensity);
  306.     */
  307. }
  308.  
  309. /*
  310.  * Remove a light from a light source list. However,
  311.  * this routine doesn't actually delete it.
  312.  */
  313. void
  314. LtRemove( LmLighting *lighting, LtLight *light )
  315. {
  316.     LtLight *l;
  317.  
  318.     if( lighting->lights == light ) {
  319.     lighting->lights = lighting->lights->next;
  320.     }
  321.     else {
  322.     for( l=lighting->lights; l->next; l=l->next )
  323.         if( l->next == light ) {
  324.         l->next = l->next->next;
  325.         break;
  326.         }
  327.     }
  328. }
  329.  
  330. void
  331. LtAppend( LmLighting *lighting, LtLight *light )
  332. {
  333.     LtLight *l;
  334.  
  335.     if( lighting->lights ) {
  336.     for( l=lighting->lights; l->next; l=l->next )
  337.         ;
  338.     l->next = light;
  339.     }
  340.     else
  341.     lighting->lights = light;
  342. }
  343.  
  344. /*
  345.  * Create a list of lights and a lighting model.
  346.  */
  347. LmLighting *
  348. _LmSet(LmLighting *lgt, int a1, register va_list *alist)
  349. {
  350.     int attr;
  351.     Color *co;
  352.     LtLight *la;
  353.     int v, mask;
  354.     char **ablock = NULL;
  355.  
  356. #define NEXT(type) OOGL_VA_ARG(type,alist,ablock)
  357.  
  358.     if (!alist) return lgt;
  359.     if (lgt == NULL) {
  360.       /*
  361.        * New Lighting created here.
  362.        */
  363.       lgt =  OOGLNewE(LmLighting, "LmCreate Lighting");
  364.       LmDefault(lgt);
  365.     }
  366.  
  367.     for(attr = a1; attr != LM_END; attr = NEXT(int)) {
  368.       switch (attr) { /* parse argument list */
  369.       case LM_ABLOCK:
  370.     ablock = NEXT(char **);
  371.     break;
  372.       case LM_LtSet:
  373.     la = ablock ? LtSet(NULL, LT_ABLOCK, ablock)
  374.       : _LtSet(NULL, va_arg(*alist, int), alist);
  375.     la->next = lgt->lights;
  376.     lgt->lights = la;
  377.     break;
  378.       case LM_LIGHT:
  379.     lgt->lights = NEXT(LtLight *);
  380.     break;
  381.       case LM_REPLACELIGHTS:
  382.     if (NEXT(int))
  383.       lgt->valid |= LMF_REPLACELIGHTS;
  384.     else
  385.       lgt->valid &= ~LMF_REPLACELIGHTS;
  386.     break;
  387.       case LM_AMBIENT:
  388.     co = NEXT(Color *);
  389.     CoCopy(co, &lgt->ambient);
  390.     lgt->valid |= LMF_AMBIENT;
  391.     break;
  392.       case LM_LOCALVIEWER:
  393.     lgt->localviewer = NEXT(double);
  394.     lgt->valid |= LMF_LOCALVIEWER;
  395.     break;
  396.       case LM_ATTENC:
  397.     lgt->attenconst = NEXT(double);
  398.     lgt->valid |= LMF_ATTENC;
  399.     break;
  400.       case LM_ATTENM:
  401.     lgt->attenmult = NEXT(double);
  402.     lgt->valid |= LMF_ATTENM;
  403.     break;
  404.       case LM_OVERRIDE:
  405.     lgt->override |= NEXT(int);
  406.     break;
  407.       case LM_NOOVERRIDE:
  408.     lgt->override &= ~NEXT(int);
  409.     break;
  410.       case LM_INVALID:
  411.     lgt->valid &= ~NEXT(int);
  412.     break;
  413.       default:
  414.     OOGLError (0, "_LmSet: undefined option: %d\n", attr);
  415.     return NULL;
  416.     break;
  417.       }
  418.     }
  419.  
  420.     return lgt;
  421.  
  422. #undef NEXT
  423. }
  424.  
  425. LmLighting *
  426. LmCreate(int attr, ... )
  427. {
  428.     va_list alist;
  429.     LmLighting *lgt;
  430.      
  431.     va_start(alist,attr);
  432.     lgt = _LmSet(NULL, attr, &alist);
  433.     va_end(alist);
  434.     return lgt;
  435. }
  436.  
  437.  
  438. LmLighting *
  439. LmSet(LmLighting *lgt, int a1, ... )
  440. {
  441.     va_list alist;
  442.     va_start(alist,a1);
  443.     lgt = _LmSet(lgt, a1, &alist);
  444.     va_end(alist);
  445.     return lgt;
  446. }
  447.  
  448.  
  449. int
  450. LmGet(LmLighting *lgt, int attr, void *value)
  451. {
  452.     if (!lgt) return NULL;
  453.  
  454.     switch (attr) {
  455.       case LM_LIGHT:
  456.     *(LtLight **) value = lgt->lights;
  457.     break;
  458.       case LM_REPLACELIGHTS:
  459.     *(int*)value = lgt->valid & LMF_REPLACELIGHTS;
  460.     break;
  461.       case LM_AMBIENT:
  462.     *(Color *) value = lgt->ambient;
  463.     break;
  464.       case LM_LOCALVIEWER:
  465.     *(double *) value = lgt->localviewer;
  466.     break;
  467.       case LM_ATTENC:
  468.     *(double *) value = lgt->attenconst;
  469.     break;
  470.       case LM_ATTENM:
  471.     *(double *) value = lgt->attenmult;
  472.     break;
  473.       case LM_ATTEN2:
  474.     *(double *) value = lgt->attenmult2;
  475.     break;
  476.       case LM_OVERRIDE:
  477.       case LM_NOOVERRIDE:
  478.     *(int *) value = lgt->override;
  479.     break;
  480.       case LM_VALID:
  481.       case LM_INVALID:
  482.     *(int *) value = lgt->valid;
  483.     break;
  484.       default:
  485.     OOGLError (0, "LmGet: undefined option: %d\n", attr);
  486.     return -1;
  487.     break;
  488.       }
  489.     return 1;
  490. }
  491.  
  492. LmLighting *
  493. LmMerge(LmLighting *src, LmLighting *dst, int mergeflags)
  494. {
  495.     unsigned int mask;
  496.  
  497.     if(dst == NULL)
  498.     return LmCopy(src, NULL);
  499.  
  500.     mask = src ?
  501.     (mergeflags & APF_OVEROVERRIDE) ?
  502.         src->valid : src->valid & ~(dst->override &~ src->override)
  503.     : 0;
  504.  
  505.     if(src == NULL || (mask == 0 && src->lights == NULL)) {
  506.     RefIncr((Ref *)dst);
  507.     return dst;
  508.     }
  509.  
  510.     if(mask && !(mergeflags & APF_INPLACE))
  511.     dst = LmCopy(dst, NULL);
  512.     dst->changed |= src->changed;
  513.     dst->valid = (src->valid & mask) | (dst->valid & ~mask);
  514.     dst->override = (src->override & mask) | (dst->override & ~mask);
  515.     if(mask & LMF_LOCALVIEWER) dst->localviewer = src->localviewer;
  516.     if(mask & LMF_AMBIENT) dst->ambient = src->ambient;
  517.     if(mask & LMF_ATTENC) dst->attenconst = src->attenconst;
  518.     if(mask & LMF_ATTENM) dst->attenmult = src->attenmult;
  519.     if(mask & LMF_ATTEN2) dst->attenmult2 = src->attenmult2;
  520.     if(src->lights != dst->lights) {
  521.     if((mask & LMF_REPLACELIGHTS) && dst->lights) {
  522.         /* LMF_REPLACELIGHTS: replace lights rather than merging with them */
  523.         LtDeletelist(dst->lights);
  524.         dst->lights = NULL;
  525.     }
  526.     if(src->lights) {
  527.         LtLight *sl;
  528.         register LtLight *hl;
  529.  
  530.         hl = sl = LtCopylist(src->lights, 1); /* merge not copy */
  531.         while(sl->next)
  532.         sl = sl->next;
  533.         sl->next = dst->lights;
  534.         dst->lights = hl;
  535.     }
  536.     }
  537.  
  538.     RefIncr((Ref *)dst);
  539.     return dst;
  540. }
  541.  
  542. LmLighting *
  543. LmCopy(register LmLighting *from, register LmLighting *to)
  544. {
  545.   if (!from) return NULL;
  546.     if(to == NULL) {
  547.     to = OOGLNewE(LmLighting, "LmCopy LmLighting");
  548.     *to = *from;
  549.     RefInit((Ref *)to, LIGHTINGMAGIC);
  550.     to->Private = 0;
  551.     to->lights = LtCopylist(from->lights, 0);
  552.     } else if (from != to) {
  553.     Ref r;
  554.     r = *(Ref *)to;
  555.     LtDeletelist(to->lights);
  556.     *to = *from;
  557.     to->lights = LtCopylist(from->lights, 0);
  558.     *(Ref *)to = r;
  559.     }
  560.  
  561.     return to;
  562. }
  563.  
  564. void
  565. LmDefault( LmLighting *l ) 
  566. {
  567.   RefInit((Ref *)l, LIGHTINGMAGIC);
  568.   l->valid = l->override = 0;
  569.   l->ambient = black;
  570.   l->localviewer = 1;
  571.   l->attenconst = 0.0;
  572.   l->attenmult = 0.0;
  573.   l->lights = NULL;
  574.   l->changed = 1;
  575.   l->Private = 0;
  576. }
  577.  
  578. /*
  579.  * Delete a list of lights and all the lights in the list.
  580.  */
  581. void
  582. LmDelete(LmLighting *lm)
  583. {
  584.     register LtLight *l, *nl;
  585.  
  586.     if(lm == NULL || RefDecr((Ref *)lm) > 0)
  587.     return;
  588.     LtDeletelist(lm->lights);
  589.     free(lm);
  590. }
  591.  
  592. void
  593. LtDeletelist(register LtLight *l)
  594. {
  595.     register LtLight *nl;
  596.  
  597.     while(l) {
  598.     nl = l->next;
  599.     LtDelete(l);
  600.     l = nl;
  601.     }
  602. }
  603.  
  604. LtLight *
  605. LtCopylist(LtLight *l, int mergeflag)
  606. {
  607.     register LtLight **tailp;
  608.     LtLight *new;
  609.  
  610.     for(tailp = &new; l != NULL; l = l->next) {
  611.         if (mergeflag & APF_INPLACE) 
  612.       *tailp = LtMerge(l, NULL);
  613.         else 
  614.       *tailp = LtCopy(l, NULL);
  615.     tailp = &(*tailp)->next;
  616.     }
  617.     *tailp = NULL;
  618.     return new;
  619. }
  620.  
  621. #define max(a,b) (a)>(b)?(a):(b)
  622.  
  623. static void
  624. norm( color, coeff )
  625.     Color *color;
  626.     float *coeff;
  627. {
  628.     *coeff = max(color->r, color->g);
  629.     *coeff = max(color->b, *coeff);
  630.  
  631.     if( *coeff != 0.0 ) {
  632.     color->r /= *coeff;
  633.     color->g /= *coeff;
  634.     color->b /= *coeff;
  635.     }
  636. }
  637.  
  638. /*
  639.  * Load a lighting description from a file.
  640.  */
  641. LmLighting *
  642. LmLoad(LmLighting *li, char *name)
  643. {
  644.     FILE *f;
  645.     LmLighting *ls;
  646.  
  647.     f = fopen(name,"r");
  648.     if(!f) {
  649.     perror(name);
  650.     return 0;
  651.     }
  652.     ls = LmFLoad(li, f, name);
  653.     fclose(f);
  654.     return ls;
  655. }
  656.  
  657.  
  658. /*
  659.  * Load Lighting from file.
  660.  * Syntax:
  661.  *    < "filename_containing_material"    [or]
  662.  *    {   keyword  value   keyword  value   ...  }
  663.  *
  664.  *   Each keyword may be prefixed by "*", indicating that its value should
  665.  *   override corresponding settings in child objects.  [By default,
  666.  *   children's appearance values supercede those of their parents.]
  667.  *
  668.  */
  669.  
  670. LmLighting *
  671.   LmFLoad(lgt, f, fname)
  672. LmLighting *lgt;
  673. FILE *f;
  674. char *fname;    /* Used for error msgs, may be NULL */
  675. {
  676.   char *w;
  677.   register int i;
  678.   float v[3];
  679.   int brack = 0;
  680.   int over, not;
  681.   static char *lkeys[] = {
  682.     "ambient", "localviewer", "attenconst", "attenmult", "attenmult2", "light",
  683.     "replacelights" 
  684.     };
  685.   static char largs[] = { 3, 1, 1, 1, 1, 0, 0};
  686.   static unsigned short lbits[] = {
  687.     LMF_AMBIENT, LMF_LOCALVIEWER, LMF_ATTENC, LMF_ATTENM, LMF_ATTEN2, 0, LMF_REPLACELIGHTS
  688.     };
  689.   int got;
  690.   LmLighting l;
  691.   LtLight lite;
  692.   LtLight *last, *new;
  693.   
  694.   LmDefault(&l);
  695.   
  696.   over = not = 0;
  697.   new = l.lights;
  698.   last = NULL;
  699.  
  700.   for(;;) {
  701.     switch(fnextc(f, 0)) {
  702.     case '<':
  703.       fgetc(f);
  704.       if(LmLoad(&l, ftoken(f, 0)) == NULL) return NULL;
  705.       if(!brack) goto done;
  706.       break;
  707.     case '{': brack++; fgetc(f); break;
  708.     case '}': if(brack) { fgetc(f); } goto done;
  709.     case '*': over = 1; fgetc(f); break;        /* 'override' prefix */
  710.     case '!': not = 1; fgetc(f); break;
  711.     default:
  712.       w = ftoken(f, 0);
  713.       if(w == NULL)
  714.     return LmCopy(&l, lgt);
  715.       /* break;    */                /* done */
  716.       
  717.       for(i = sizeof(lkeys)/sizeof(lkeys[0]); --i >= 0; )
  718.     if(!strcmp(w, lkeys[i]))
  719.       break;
  720.       
  721.       if( i < 0) {
  722.     OOGLError(1, "LmFLoad: %s: unknown lighting keyword %s",fname,w);
  723.     return NULL;
  724.       } else if( !not && (got=fgetnf(f, largs[i], v, 0)) != largs[i] ) {
  725.     OOGLError(1, "LmFLoad: %s: \"%s\" expects %d values, got %d",
  726.           fname, w, largs[i], got);
  727.     return NULL;
  728.       }
  729.       
  730.       if(not) {
  731.     if(!over) l.valid &= ~lbits[i];
  732.     l.override &= ~lbits[i];
  733.       } else {
  734.     l.valid |= lbits[i];
  735.     if(over) l.override |= lbits[i];
  736.     switch(i) {
  737.     case 0: l.ambient = *(Color *)v; break;
  738.     case 1: l.localviewer = v[0]; break;
  739.     case 2: l.attenconst = v[0]; break;
  740.     case 3: l.attenmult = v[0]; break;
  741.     case 4: l.attenmult2 = v[0]; break;
  742.     case 5:
  743.       LtFLoad( &lite, f, fname );
  744.       new = LtCreate(LT_END);
  745.       if (!last)
  746.         l.lights = new;
  747.       else
  748.         last->next = new;
  749.       LtCopy( &lite, new);
  750.       last = new;
  751.       new = new->next;
  752.       break;
  753.     }
  754.       }
  755.       over = not = 0;
  756.     }
  757.   }
  758.  done:
  759.   return LmCopy(&l, lgt);
  760. }
  761.  
  762.  
  763. /*
  764.  * Save a light description in a file.
  765.  */
  766.  
  767. LmFSave(LmLighting *li, FILE *f, char *fname)
  768. {
  769.     register LtLight *la;
  770.  
  771.     fprintf(f,"\tambient %g %g %g\n", 
  772.     li->ambient.r,
  773.     li->ambient.g,
  774.     li->ambient.b);
  775.     fprintf(f,"\tlocalviewer %d\n",li->localviewer);
  776.     fprintf(f,"\tattenconst %g\n",li->attenconst);
  777.     fprintf(f,"\tattenmult %g\n",li->attenmult);
  778.     if(li->valid & LMF_ATTEN2) fprintf(f,"\tattenmult2 %g\n",li->attenmult2);
  779.     if (li->valid & LMF_REPLACELIGHTS) fprintf(f,"\treplacelights\n");
  780.     la = li->lights;
  781.     while(la) {
  782.     fprintf(f, "\tlight {\n");
  783.     LtFSave( la, f );
  784.     fprintf(f, "\t}\n");
  785.     la = la->next;
  786.     }
  787. }
  788.